/*************************************************************************
 *
 *  Copyright (C) 2013 Florian Dang <florian.coin@gmail.com>
 *
 *  This file is part of NatusVerse.
 *
 *  NatusVerse is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  NatusVerse is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with NatusVerse.  If not, see <http://www.gnu.org/licenses/>.
 *
 *************************************************************************/

#include "raytracer/scene.h"

#include <stdio.h>
#include <cmath>
#include <cassert>

#include "raytracer/objects/sphere.h"
#include "raytracer/objects/plane.h"
#include "raytracer/objects/material.h"

namespace raytracer
{
Scene Scene::TestScene1(int w, int h)
{
	Scene scene;

	Material mat1(Material::kGouraud, color::blueviolet, 0.9f, color::blueviolet, 0.9f, 0.5f, 200.0f);
	Point centre_sphere1(1.0f, 60.0f, -4.0f);
	Sphere sph1(mat1, centre_sphere1, 4.0f);

	// Plane plane1(mat1, centre_sphere1, centre_sphere1);

	Material mat2(Material::kGouraud, color::greenyellow, 0.8f, color::greenyellow, 0.9f, 0.9f, 900.0f);
	Point centre_sphere2(4.0f, 20.0f, -4.0f);
	Sphere sph2(mat2, centre_sphere2, 2.0f);

	Material mat3(Material::kGouraud, color::cyan);
	Point centre_sphere3(6.0f, 40.0f, -5.0f);
	Sphere sph3(mat3, centre_sphere3, 3.0f);

	Material mat4(Material::kGouraud, color::white);
	Point centre_sphere4(0.0f, 200.0f, 3.0f);
	Sphere sph4(mat4, centre_sphere4, 40.0f);

	Point orig_light(-300.0f, -200.0f, -10.0f);
	Light light(orig_light, 1.0f, 1.0f, 1.0f);
	Point orig_light2(5.0f, -400.0f, 40.0f);
	Light light2(orig_light2, 0.6f, 0.8f, 0.4f);

	Point  eye(0.0f, 0.0f, 0.0f);
	Vector la(0.0f, 1.0f, 0.0f);
	Vector vup(0.0f, 0.0f, 1.0f);

	Camera camera(eye, la, vup, 1.0f, w, h);

	scene.AddObject(&sph1);
	scene.AddObject(&sph2);
	scene.AddObject(&sph3);
	// scene.AddObject(&sph4);
	scene.AddLight(light);
	scene.AddLight(light2);
	scene.set_camera(camera);

	scene.set_num_objects();
	scene.set_num_lights();

	return scene;
}



Scene Scene::TestScene2(int w, int h)
{
	Scene scene;

	Material mat1(Material::kGouraud, color::blueviolet, 0.9f, color::blueviolet, 0.9f, 0.5f, 200.0f);
	Point centre_sphere1(1.0f, 60.0f, -4.0f);
	Sphere sph1(mat1, centre_sphere1, 4.0f);

	// Plane plane1(mat1, centre_sphere1, centre_sphere1);

	Material mat2(Material::kGouraud, color::greenyellow, 0.8f, color::greenyellow, 0.9f, 0.9f, 900.0f);
	Point centre_sphere2(4.0f, 20.0f, -4.0f);
	Sphere sph2(mat2, centre_sphere2, 2.0f);

	Material mat3(Material::kGouraud, color::cyan);
	Point centre_sphere3(6.0f, 40.0f, -5.0f);
	Sphere sph3(mat3, centre_sphere3, 3.0f);

	Material mat4(Material::kGouraud, color::white);
	Point centre_sphere4(0.0f, 200.0f, 3.0f);
	Sphere sph4(mat4, centre_sphere4, 40.0f);

	Point orig_light(-400.0f, -100.0f, -10.0f);
	Light light(orig_light, 1.0f, 1.0f, 1.0f);
	Point orig_light2(5.0f, -400.0f, 40.0f);
	Light light2(orig_light2, 0.6f, 0.8f, 0.4f);

	Point  eye(0.0f, 0.0f, 0.0f);
	Vector la(0.0f, 1.0f, 0.0f);
	Vector vup(0.0f, 0.0f, 1.0f);

	Camera camera(eye, la, vup, 1.0f, w, h);

	scene.AddObject(&sph1);
	scene.AddObject(&sph2);
	scene.AddObject(&sph3);
	// scene.AddObject(&sph4);
	//scene.AddLight(light);
	scene.AddLight(light2);
	scene.set_camera(camera);

	scene.set_num_objects();
	scene.set_num_lights();

	return scene;
}

void Scene::AddObject(Object* object)
{
	// this clone is really tricky...
	vec_object_.push_back(object->clone());
}

void Scene::AddLight(Light light)
{
	vec_light_.push_back(light);
}

bool Scene::RemoveLight(int index)
{

	assert(index < static_cast<int>(vec_light_.size()));
	vec_light_.erase(vec_light_.begin() + index);
	//vec_light_.pop_back();

	return true;
}

void Scene::MoveLightX(int index, float begin, float end, float& sign)
{
	vec_light_[index].MoveX(begin, end, sign);
}

void Scene::NearestObject(const Ray& ray_view, Object*& nearest_object, float& nearest_t) const
{
	float t = nearest_t;

	// for (int i = 0; i < num_objects(); ++ i) // optimized num_objects() access
//	for (VecObject::iterator current_object = vec_object().begin();
//			 	 current_object != vec_object().end();
//			 	 ++ current_object)
	for (auto current_object : vec_object())
	{
		// Object *current_object = vec_object()[i];
		if(current_object->Intersect(ray_view, t))
		{
			if (t < nearest_t)
			{
				nearest_t = t;
				nearest_object = current_object;
			}
		}
	}
}

bool Scene::IsInShadow(const Ray& ray_light) const
{
	//float t = sqrtf(ray_light.direction() * ray_light.direction());
	float t = Object::kInfinite;

	for (int i = 0; i < num_objects(); ++ i) // optimized num_objects() access
	{
		if((vec_object()[i])->Intersect(ray_light, t) == true)
		{
			return true;
		}
	}

	return false;
}

void Scene::Print() const
{
	printf("(i) Displaying scene...\n");
	printf("(i) Camera: ");
	camera().Print();
	printf("\n(i) Displaying objects...");
	for (int i = 0; i < static_cast<int>((vec_object()).size()); i ++)
	{
		printf("\n  [%d] ", i);
		vec_object()[i]->Print();
	}
	printf("\n(i) Displaying Lights...");
	for (int i = 0; i < static_cast<int>(vec_light().size()); i ++)
	{
		printf("\n  [%d] ", i);
		vec_light()[i].Print();
	}
	printf("\n");
}
}
